// 14. 多线程

/**
 * 1. 什么是多线程
 *      多线程是多任务处理的一种特殊形式，多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下，两种类型的多任务处理：基于进程和基于线程。

      基于进程的多任务处理是程序的并发执行。
      基于线程的多任务处理是同一程序的片段的并发执行。
 *       多线程程序包含可以同时运行的两个或多个部分。这样的程序中的每个部分称为一个线程，每个线程定义了一个单独的执行路径。
 *
 * 2. #21 pthread使用
 *    头文件 pthread.h
 *    注意:
 *      编译指令加上 -lpthread参数
 *      g++ 5_2.cpp -lpthread -o ./build/5_1.o
 *    相关方法:
 *        创建线程
 *            语法: int pthread_create(pthread_t *th, const pthread_attr_t *attr, void *(*func)(void *), void *arg);
 *            返回值: 为0代表创建成功，不为0创建失败
 *            注意: 参数是无类型指针，线程函数中时，按原来类型进行转换，多参数可以传一个结构体。
 *
 *        终止线程
 *            语法: void pthread_exit(void *res)
 *            注意: 主线程中调用，主线程会等所有**在主线程创建**的线程执行完在终止线程，如果不加的话，主线程停了，子线程都停了。
 *
 *        初始化线程属性
 *            语法: int pthread_attr_init(pthread_attr_t *attr)
 *
 *        设置线程属性
 *            语法: int pthread_attr_setdetachstate(pthread_attr_t *a, int flag)
 *            注意: flag 为 PTHREAD_CREATE_JOINABLE,使用该属性用pthread_create创建的线程为可连接的，flag 为 PTHREAD_CREATE_DETACHED，创建的线程是不可连接的。
 *
 *        删除线程属性
 *            语法: int pthread_attr_destroy(pthread_attr_t *attr)
 *
 *        连接线程
 *            语法: int pthread_join(pthread_t t, void **res)
 *            返回值: 为0代表连接成功，不为0连接失败
 *            说明: pthread_join() 子程序阻碍调用程序，直到指定的 threadid 线程终止为止
 *            注意: 只有创建时定义为可连接的线程才可以被连接,如果线程创建时被定义为可分离的，则它永远也不能被连接
 *
 *        分离线程
 *            语法: int pthread_detach(pthread_t t)
 *
 * 3. #31 thread库使用
 *     使用前提: c++11及以上版本使用, MinGW-w64 thread为posix  编译器.vscode中的 c_cpp_properties.json、launch.json都要改
 *     头文件: #include<thread>
 *     相关方法:
 *         创建线程
 *            语法: std::thread td(threadHandler,..argvs);
 *            示例:
 *                 std::thread threads[5];
 *                 threads[i] = std::thread(threadHandler, &tds[i],a,b);
 *          获取当前线程id
 *            语法: std::thread::id main_t_id = std::this_thread::get_id();
 *
 *          当前线程暂停多少秒
 *             语法: void std::this_thread::sleep_for<int64_t, std::ratio<1LL>>(const std::chrono::seconds &__rtime)
 *             示例: std::this_thread::sleep_for(std::chrono::seconds(5));
 *
 *           查看该线程可以并发执行多少个
 *              方法一语法: static unsigned int std::thread::hardware_concurrency()
 *              方法二语法: std::thread::native_handle_type std::thread::native_handle()
 *
 *            连接线程
 *               语法: void std::thread::join()
 *
 *            分离线程
 *               语法: void std::thread::detach()
 * 4. #41 window win32
 *        头文件:
 *              #include <stdafx.h>
 *              #include <windows.h>
 *          注意: stdafx.h引入失败的话，换 #include <stdio.h> #include <tchar.h>
 *        相关方法:
 *            创建线程
 *               语法: HANDLE CreateThread(LPSECURITY_ATTRIBUTES lpThreadAttributes, SIZE_T dwStackSize, LPTHREAD_START_ROUTINE lpStartAddress, LPVOID lpParameter, DWORD dwCreationFlags, LPDWORD lpThreadId)
 *               参数:
 *                  lpThreadAttributes：指向SECURITY_ATTRIBUTES结构的指针，决定返回的句柄是否可被子进程继承，如果为NULL则表示返回的句柄不能被子进程继承。
 *                  dwStackSize ：线程栈的初始化大小，字节单位。系统分配这个值对

 *                  lpStartAddress：指向一个函数指针，该函数将被线程调用执行。因此该函数也被称为线程函数(ThreadProc)，是线程执行的起始地址，线程函数是一个回调函数，由操作系统在线程中调用。线程函数的原型如下：

       DWORD WINAPI ThreadProc(LPVOID lpParameter){... return 0L;}    //lpParameter是传入的参数，是一个空指针

 *                  lpParameter：传入线程函数(ThreadProc)的参数，不需传递参数时为NULL

 *                  dwCreationFlags：控制线程创建的标志，有三个类型，0：线程创建后立即执行线程；CREATE_SUSPENDED：线程创建后进入就绪状态，直到线程被唤醒时才调用；STACK_SIZE_PARAM_IS_A_RESERVATION：dwStackSize 参数指定线程初始化栈的大小，如果STACK_SIZE_PARAM_IS_A_RESERVATION标志未指定，dwStackSize将会设为系统预留的值。 
 *                  lpThreadId： 传出参数，用于获得线程ID，如果为NULL则不返回线程ID
 * 
 *              返回值: 如果线程创建成功，则返回这个新线程的句柄，否则返回NULL。如果线程创建失败，可通过GetLastError函数获得错误信息。
 *            
 *            关闭一个打开的对象句柄(线程句柄)
 *               语法: WINBOOL CloseHandle(HANDLE hObject)
 * 
 *            获取错误信息
 *               语法: DWORD GetLastError()
 *            
 *            创建一个互斥量锁
 *               语法: HANDLE CreateMutex(LPSECURITY_ATTRIBUTES lpMutexAttributes, WINBOOL bInitialOwner, LPCSTR lpName)
 *               示例: HANDLE g_hMutex = CreateMutex(NULL,false,NULL);
 *            
 *            等待获取一个互斥量锁
 *                语法: DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
 *                示例: WaitForSingleObject(g_hMutex,INFINITE)
 *                说明:如果超时时间间隔(毫秒)dwMilliseconds非0,则等待时间间隔用完或对象变为非占用状态；如果dwMilliseconds为INFINITE，则表示为无限等待，直到等到对象处于非占用状态。
 *            
 *            等待多个互斥量锁
 *                 语法: DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, WINBOOL bWaitAll, DWORD dwMilliseconds)
 *                 说明: 它的作用是等待一个或所有指定的对象，直到所有的对象处于非占用的状态，或超出设定的时间间隔。
 * 
 *             释放互斥量锁
 *                  语法: WINBOOL ReleaseMutex(HANDLE hMutex)
 *
 * 扩展：
 * 1. memset初始化字符数组，memset需要 string.h头文件
 *     语法: void *memset(void *_Dst,int _Val,size_t _Size);
 *     示例: memset(name,'\0',sizeof(char) * NAME_SIZE);
 *
 * 2. 自定义转字符串函数
 *      头文件: #include <strstream>
          template <class T>
          std::string convertToStr(T x){
            strstream ss;
            ss << x;
            std::string str;
            ss >> str;
            return str;
          }

          转成char* convertToStr(x).c_str()
 *
 *
 *
 * 疑问
 *    1. pthread_detach()如何使用
*/
#include <iostream>

#define __mark 41

#if 21 == __mark
/**
 * #21 pthread使用
 */
#include <pthread.h>
#include <string.h>
#include <unistd.h>
#include <strstream>
#define NAME_SIZE 40

pthread_mutex_t p_mutex; // 创建互斥锁

typedef struct __THREAD_DATA
{
  char name[NAME_SIZE];
  int n;
  __THREAD_DATA() : n(0)
  {
    memset(name, 0, NAME_SIZE * sizeof(char)); // memset 需要 string.h头文件
  }
} THREAD_DATA;
void *pthreadHandler(void *pthreadParameter)
{
  THREAD_DATA data = *((THREAD_DATA *)pthreadParameter);

  if (data.n == 6)
  {
    // 创建子线程
    pthread_t pt;
    THREAD_DATA td;
    td.n = 3 * 3;
    strcpy(td.name, "线程3-1");
    // pthread_attr_t attr;
    // pthread_attr_init(&attr);
    // pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
    int res = pthread_create(&pt, NULL, pthreadHandler, &td);
    if (0 == res)
    {
      std::cout << "创建子线3-1程成功!" << std::endl;
    }
    // pthread_join(pt,NULL);
  }
  for (int i = 0; i <= data.n; i++)
  {
    pthread_mutex_lock(&p_mutex); // 避免线程同时占用终端
    std::cout << data.name << "--------" << i << std::endl;
    pthread_mutex_unlock(&p_mutex);
    sleep(1);
    // pthread_exit(NULL);// 线程提前退出
  }

  std::cout << data.name << " exit..." << std::endl;
  pthread_exit(NULL); // 退出线程

  return 0;
}
template <class T>
std::string convertToString(T x)
{
  std::strstream ss;
  ss << x;
  std::string str;
  ss >> str;
  return str;
}
int main(void)
{
  const int nums = 5;
  pthread_t pids[nums];

  // 初始化属性，并设置线程可连接
  pthread_attr_t attr;
  pthread_attr_init(&attr);
  pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
  // pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

  THREAD_DATA tds[nums];
  for (int i = 0; i < nums; i++)
  {
    strcpy(tds[i].name, strcat(strcpy(tds[i].name, "线程"), convertToString(i + 1).c_str()));
    tds[i].n = (i + 1) * 2;
    pthread_mutex_lock(&p_mutex);
    int res = pthread_create(&pids[i], &attr, pthreadHandler, &tds[i]);
    // int res = pthread_create(&pids[i], NULL, pthreadHandler, &tds[i]);
    if (res == 0)
    {
      std::cout << "create thread " << i + 1 << " success" << std::endl;
    }
    else
    {
      std::cerr << "Error: create thread " << i + 1 << "failed!" << std::endl;
    }
    pthread_mutex_unlock(&p_mutex);
  }
  // /**
  pthread_attr_destroy(&attr);
  void *status;
  for (int i = 0; i < nums; i++)
  {
    int res = pthread_join(pids[i], &status);
    if (res == 0)
    {
      std::cout << tds[i].name << "joined success. status " << status << std::endl;
    }
    else
    {
      std::cerr << "Error: " << tds[i].name << "joined falied. status " << status << std::endl;
    }

    int res = pthread_detach(pids[i]);
    // if (res == 0)
    // {
    //   std::cout << tds[i].name << "detached success." << std::endl;
    // }
    // else
    // {
    //   std::cerr << "Error: " << tds[i].name << "detached falied." << std::endl;
    // }
  }
  // */

  std::cout << "main exit..." << std::endl;
  // pthread_exit(NULL); // 等待所有线程结束，退出主程序，下面代码不执行了

  printf("-----------end-------------\n");
  return 0;
}

#elif 31 == __mark
/**
 * #31 thread 库使用
 */
#define NAME_SIZE 40
#include <thread>
#include <string.h>
#include <windows.h>
#include <strstream>
typedef struct __THREAD_DATA
{
  int id = 0;
  int nums = 0;
  char name[NAME_SIZE];
  __THREAD_DATA()
  {
    memset(name, '\0', sizeof(char) * NAME_SIZE);
  }
} THREAD_DATA;

std::thread::id main_t_id = std::this_thread::get_id();
void waitForSeconds(int sec)
{

  std::cout << "the thread is wait for " << sec << " seconds !" << std::endl;
  // if (std::this_thread::get_id() == main_t_id)
  // {
  //   std::cout << "it is main thread !" << std::endl;
  // }
  // else
  // {
  //   std::cout << "it is not main thread !" << std::endl;
  // }

  std::this_thread::sleep_for(std::chrono::seconds(sec));
  // std::this_thread::sleep_for(std::chrono::seconds(INFINITE));
}
void threadHandler(void *pthreadParameter)
// void threadHandler(void *pthreadParameter, int a, int b)
{
  // std::cout << "a : " << a << " b : " << b << std::endl;
  THREAD_DATA data = *((THREAD_DATA *)pthreadParameter);
  for (int i = 0; i < data.nums; i++)
  {
    std::cout << data.name << "--------" << i + 1 << std::endl;
    Sleep(1000);
  }
  if (data.id == 2)
  {
    waitForSeconds(5);
    std::cout << "sleep ended !" << std::endl;
  }
}

template <class T>
std::string convertToStr(T x)
{
  std::strstream ss;
  ss << x;
  std::string str;
  ss >> str;
  return str;
}
int main(void)
{
  const int nums = 5;
  // std::thread thread(threadHandler, &td);
  // std::thread thread(threadHandler, &td, 1, 2);

  std::thread threads[nums];
  THREAD_DATA tds[nums];

  for (int i = 0; i < nums; i++)
  {
    std::string t_name = "线程";
    t_name += convertToStr(i + 1);
    tds[i].id = i;
    tds[i].nums = 2 * i + 1;
    strcpy(tds[i].name, t_name.c_str());
    threads[i] = std::thread(threadHandler, &tds[i]);
  }

  std::cout << "可以并发执行多少个(不准确)该线程---" << threads[0].hardware_concurrency() << "个" << std::endl;
  std::cout << "可以并发执行多少个(不准确)该线程---" << threads[0].native_handle() << "个" << std::endl;

  // thread.join();
  int i = 0;
  for (auto &thread : threads)
  {
    // thread.join(); // 等待该线程完成
    // std::cout << "thread " << ++i << " is joined !" << std::endl;

    thread.detach();
    std::cout << "thread " << ++i << " is detached !" << std::endl;
  }
  printf("-----------end-------------\n");
  return 0;
}
#elif 41 == __mark
/**
 * #41 window win32
 */
#include <stdio.h>
#include <tchar.h>
#include <string.h>
#include <iostream>
#include <strstream>
#include <windows.h>

#define NAME_SIZE 40
#define time_interval 200

typedef struct __THREAD_DATA
{
  int id;
  int nums;
  char name[NAME_SIZE];
  __THREAD_DATA() : id(0), nums(1)
  {
    memset(name, '\0', sizeof(char) * NAME_SIZE);
  }
} THREAD_DATA;

HANDLE g_mutex; // 互斥量锁

DWORD threadHandler(LPVOID lpParameter)
{
  THREAD_DATA td = *((THREAD_DATA *)lpParameter);
  for (int i = 0; i < td.nums; i++)
  {
    // WaitForSingleObject(g_mutex, INFINITE); // 请求获取一个互斥量锁
    // WaitForMultipleObjects
    std::cout << td.name << "======" << i + 1 << std::endl;
    Sleep(time_interval);
    // ReleaseMutex(g_mutex); // 释放互斥量锁
  }
  return 0L;
}

template <typename T>
std::string convertToStr(T x)
{
  std::string s;
  std::strstream ss;
  ss << x;
  ss >> s;
  return s;
}

int main(void)
{
  g_mutex = CreateMutex(NULL, false, NULL); // 创建一个互斥锁
  const int nums = 5;
  HANDLE threads[nums];
  THREAD_DATA tds[nums];
  for (int i = 0; i < nums; i++)
  {
    tds[i].id = i;
    tds[i].nums = 2 * i + 1;
    std::string name = "线程";
    name += convertToStr(i + 1);
    strcpy(tds[i].name, name.c_str());

    threads[i] = CreateThread(NULL, 0, threadHandler, &tds[i], 0, NULL);
    // WaitForSingleObject(g_mutex, INFINITE); // 等待获取一个互斥量锁
    if (threads[i] != NULL)
    {
      std::cout << tds[i].name << "创建成功" << std::endl;
    }
    else
    {
      std::cerr << "Error:" << tds[i].name << "创建失败.message: " << GetLastError() << std::endl;
    }
    // ReleaseMutex(g_mutex); //释放互斥量锁
  }

  for (auto &thread : threads)
  {
    CloseHandle(thread); // 关闭线程
  }
  printf("--------------end----------------");
  return 0;
}
#endif